﻿#pragma once
#include <string>
#include <vector>
#include <memory>
#include "DeviceProtocol.hpp"
#include "utils/serial/AsyncSerial.h"
#include "utils/audio/AudioUtils.h"

class DeviceDataReceiver {
public:
	virtual ~DeviceDataReceiver() = default;
	virtual void OnReportLedStatus(double time, double lighteness) = 0;
};

class DeviceModel {
public:
	DeviceModel(DeviceDataReceiver* recvr, const std::wstring& devName, const std::wstring &serPath);
	virtual ~DeviceModel();
	bool Connect(unsigned int baudRate);
	bool IsConnected() const;
	bool Disconnect();
	
	bool ResetReportTime();
	bool StartReport();
	bool StopReport();
	
	// 0% ~ 100%
	bool LedSetFixedLightness(double lightness);
	
	// 500ms~30000ms
	bool LedSetSawtoothWave(unsigned int msPeriod) {
		return ConfigLedWaveMode(proto::FrameTag::PwmSawtoothWave, msPeriod);
	}
	
	bool LedSetTriangleWave(unsigned int msPeriod) {
		return ConfigLedWaveMode(proto::FrameTag::PwmTriangleWave, msPeriod);
	}

	bool LedSetSinWave(unsigned int msPeriod) {
		return ConfigLedWaveMode(proto::FrameTag::PwmSinWave, msPeriod);
	}

	bool LedSetAudioRmsMode(double factor);
	void LeaveAudioRmsMode();

protected:
	struct DevCmd {
		proto::FrameTag tag;
		std::vector<uint8_t> value;
	};
	bool SendCommands(const std::vector<DevCmd>& cmds);
	bool SendOneCommand(const DevCmd &cmd);
	bool ConfigLedWaveMode(proto::FrameTag tag, unsigned int period);

private:
	void OnRecvDeviceData(const char *dataBytes, std::size_t byteCnt);
	bool OnRecvLedStatus(const std::string& data);
	bool OnRecvDataACK(const std::string& ack);

private:
	std::wstring deviceName;
	std::wstring serialPath;
	uint8_t frameDecodeBuffer[256]{};
	uint8_t frameEncodeBuffer[256]{};
	proto::FrameDecoder protoDecoder;
	proto::FrameAssembler protoEncoder;
	std::unique_ptr<CallbackAsyncSerial> serialPort;
	std::unique_ptr<util::audio::AudioRmsReceiver> audioRecvr;
	DeviceDataReceiver* devDataRecvr = nullptr;
};